#include "AdministradorBufferingBSharp.h"
#include "WrapperBufferBSharp.h"
#include "DatoBSharp.h"
#include "ControladorAlmacenamientoBSharp.h"
#include "IndiceBSharp.h"
#include "Archivo.h"
#include <algorithm>
#include "Buffer.h"
#include "Registro.h"
#include <list>

namespace Toolkit
{

AdministradorBufferingBSharp::AdministradorBufferingBSharp(
        const TipoSerializacion tserializacion,
        const unsigned long int &tamanoBloque)
{
    this->tamanoBloque = tamanoBloque;
    this->tserializacion = tserializacion;
}

AdministradorBufferingBSharp::~AdministradorBufferingBSharp()
{

}

void AdministradorBufferingBSharp::guardarDato(const Dato &dato,
        const Clave &clave)
{
    unsigned long int posEnPrimario = busquedaInexacta(clave);

    WrapperComponente wrapperVacio;
    indice->controladorAlmacenamiento->guardarDato(*(indice->primario), dato,
            clave, posEnPrimario, wrapperVacio);

    bajarABuffer();

}

bool AdministradorBufferingBSharp::recuperarDato(const Clave &clave, Dato &dato)
{
    unsigned long int posEnPrimario = busquedaInexacta(clave);
    bool estaEnSecundario = false;
    BloqueBSharp *nodo = buscarBloque(posEnPrimario, estaEnSecundario);

    WrapperBufferBSharp wrapperBuffer(posEnPrimario);

    if (!estaEnSecundario) {
        wrapperBuffer.setComponente(nodo);
        this->indice->controladorAlmacenamiento->guardarComponente(
                (*(this->indice->secundario)), wrapperBuffer);
    }

    Registro* registro = dynamic_cast<Registro*> (nodo->buscar(clave)); //Busqueda exacta en la hoja


    bool resultado = (registro != NULL);

    if (resultado) {
        registro->hidratarDato(dato);
    }

    delete nodo;

    return resultado;
}

void AdministradorBufferingBSharp::eliminarDato(const Clave &clave, Dato &dato)
{
    unsigned long int posEnPrimario = busquedaInexacta(clave);

    WrapperComponente wrapperVacio;
    indice->controladorAlmacenamiento->eliminarDato(*(indice->primario),
            posEnPrimario, dato, clave, wrapperVacio);

    bajarABuffer();

}

BloqueBSharp *AdministradorBufferingBSharp::AdministradorBufferingBSharp::buscarBloque(
        const unsigned long int &posicion) const
{
    bool aux;
    return buscarBloque(posicion, aux);
}

BloqueBSharp *AdministradorBufferingBSharp::AdministradorBufferingBSharp::buscarBloque(
        const unsigned long int &posicion, bool &estaEnSecundario) const
{
    WrapperBufferBSharp wrapper(0);

    //trato de encontrarlo en secundario
    long int posEnSec = posicionEnSecundario(posicion);
    if ((posEnSec != -1) && (find(posiblesDesactualizados.begin(),
            posiblesDesactualizados.end(), posicion)
            == posiblesDesactualizados.end())) {
        this->indice->controladorAlmacenamiento->recuperarComponente(
                *(this->indice->secundario), posEnSec, wrapper);
        estaEnSecundario = true;
        return dynamic_cast<BloqueBSharp*> (wrapper.getComponente());
    }
    else {
        WrapperComponente wrapperPrimario;
        //Si no esta en secundario, busca posicion en primario.
        this->indice->controladorAlmacenamiento->recuperarComponente(
                *(indice->primario), posicion, wrapperPrimario);
        estaEnSecundario = false;
        return dynamic_cast<BloqueBSharp*> (wrapperPrimario.getComponente());
    }
}

unsigned long int AdministradorBufferingBSharp::busquedaInexacta(
        const Clave &clave, const unsigned long int &nivelNodo) const
{
    BloqueBSharp *nodo = buscarBloque(0);
    unsigned long int proximoNodo = 0;

    //Busqueda inexacta hasta alcanzar una hoja
    while ((!nodo->esHoja()) && (nodo->getNivelNodo() > nivelNodo)) {
        proximoNodo = nodo->nodoSiguiente(clave);
        delete nodo;
        nodo = buscarBloque(proximoNodo);
    }

    delete nodo;
    return proximoNodo;
}

long int AdministradorBufferingBSharp::posicionEnSecundario(
        const unsigned long int & posicionEnPrimario) const
{
    WrapperBufferBSharp wrapper(0);

    for (unsigned int i = 0; i < this->indice->secundario->cantElementos(); i++) {
        try {
            this->indice->controladorAlmacenamiento->recuperarComponente(
                    *(this->indice->secundario), i, wrapper);
            if (posicionEnPrimario == wrapper.getDireccion()) {
                delete wrapper.getComponente();
                return i;
            }

            delete wrapper.getComponente();

        } catch (UnidadEliminadaException) {
            continue;
        }
    }

    return -1;
}

void AdministradorBufferingBSharp::agregarComponenteAlterado(
        const unsigned long int& nRegistro)
{
    this->posiblesDesactualizados.push_back(nRegistro);
}

void AdministradorBufferingBSharp::bajarABuffer()
{

    for (list<unsigned long int>::iterator it = posiblesDesactualizados.begin(); it
            != posiblesDesactualizados.end(); it++) {

        WrapperComponente wrapperVacio;
        indice->controladorAlmacenamiento->recuperarComponente(
                (*indice->primario), *it, wrapperVacio);

        WrapperBufferBSharp wrapperBuffer(*it);
        wrapperBuffer.setComponente(wrapperVacio.getComponente());

        long int posEnSec = posicionEnSecundario(*it);
        if (posEnSec != -1) {
            indice->controladorAlmacenamiento-> guardarComponente(
                    (*indice->secundario), posEnSec, wrapperBuffer);

        }
        else {
            indice->controladorAlmacenamiento-> guardarComponente(
                    (*indice->secundario), wrapperBuffer);
        }

        delete wrapperBuffer.getComponente();
    }

    this->posiblesDesactualizados.clear();
}

void AdministradorBufferingBSharp::setIndice(IndiceBSharp *indice)
{
    this->indice = indice;
}

}
